
/**
 * @file:       param_lib.cpp
 * @author:     WubinWang
 * @contact:    wubinstu@163.com
 * @date:       2025-01-01
 * @license:    MIT License
 *
 * Copyright (c) 2025 WubinWang
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 * DISCLAIMER:
 * 1. User acknowledges all application risks and consequences arising from usage
 *    of this software are exclusively borne by the user.
 * 2. The original author shall not be held liable for any direct, indirect,
 *    incidental, special, exemplary or consequential damages.
 * 3. No warranty of fitness for particular purpose or non-infringement is provided. */


#include "param_lib.hpp"

#include "param_storage.hpp"

static inline bool nearlyEqual (float a, float b, float epsilon = std::numeric_limits<float>::epsilon ()) {
    return std::fabs (a - b) <= epsilon * std::fmax (1.0f, std::fmax (std::fabs (a), std::fabs (b)));
}

static inline bool nearlyEqual (double a, double b, double epsilon = std::numeric_limits<double>::epsilon ()) {
    return std::fabs (a - b) <= epsilon * std::fmax (1.0, std::fmax (std::fabs (a), std::fabs (b)));
}

namespace paramfw {

    flags operator| (flags a, flags b) { return static_cast<flags> (static_cast<uint8_t> (a) | static_cast<uint8_t> (b)); }

    flags operator& (flags a, flags b) { return static_cast<flags> (static_cast<uint8_t> (a) & static_cast<uint8_t> (b)); }

    bool operator!= (const value & a, const value & b) { return static_cast<bool> (!(a == b)); };

    bool operator== (const value & a, const value & b) {
        if (a._type != b._type) return false;
        switch (a._type) {
            case type::s08: return static_cast<bool> (a.s08 == b.s08);
            case type::u08: return static_cast<bool> (a.u08 == b.u08);
            case type::s16: return static_cast<bool> (a.s16 == b.s16);
            case type::u16: return static_cast<bool> (a.u16 == b.u16);
            case type::s32: return static_cast<bool> (a.s32 == b.s32);
            case type::u32: return static_cast<bool> (a.u32 == b.u32);
            case type::s64: return static_cast<bool> (a.s64 == b.s64);
            case type::u64: return static_cast<bool> (a.u64 == b.u64);
            case type::f32: return static_cast<bool> (nearlyEqual (a.f32, b.f32));
            case type::f64: return static_cast<bool> (nearlyEqual (a.f64, b.f64));
            case type::str: return static_cast<bool> (std::strncmp (a.str, b.str, kMaxSize) == 0);
            case type::nil: return static_cast<bool> (std::memcmp (a.buf, b.buf, kMaxSize) == 0);
        }
        return false;
    }
}  // namespace paramfw

namespace paramfw {
    bool registry::save () {
        if (!this->_storage) return false;
        return this->save_impl ();
    }

    bool registry::load () {
        if (!this->_storage) return false;
        return this->load_impl ();
    }

    bool registry::subscribe (callback cb) {
        for (auto & c : this->_cbs)
            if (!c) {
                c = cb;
                return true;
            }
        return false;
    }

    bool registry::init (const meta _m[], uint32_t _c, storage * _s) {
        if (_c > kMaxParams || !_m) return false;
        this->_count = _c;
        for (uint32_t i = 0; i < _c; ++i) {
            this->_nodes[i]._meta    = _m[i];
            this->_nodes[i]._current = _m[i]._default;  // 初始为默认参数
        }

        if (this->_storage = _s; this->_storage)
            if (!this->load ())
                this->save ();
        return true;
    }

    void registry::reset_to_default () {
        for (uint32_t i = 0; i < this->_count; ++i)
            if (this->_nodes[i]._current != this->_nodes[i]._meta._default) {
                auto oldv                = this->_nodes[i]._current;
                this->_nodes[i]._current = this->_nodes[i]._meta._default;

                // 重置也是一种数值变动, 当该参数有可读权限时广播变动
                if ((this->_nodes[i]._meta._flags & flags::F_RD) == flags::F_RD)
                    this->notify (this->_nodes[i]._meta, oldv, this->_nodes[i]._meta._default);
            }
    }

    const node * registry::find (const char * name) const {
        for (uint32_t i = 0; i < this->_count; ++i)
            if (std::strncmp (this->_nodes[i]._meta._name.c_str (), name, kMaxName) == 0) {
                if ((this->_nodes[i]._meta._flags & flags::F_RD) == flags::F_RD)
                    return &this->_nodes[i];
            }
        return nullptr;
    }

    bool registry::get (const char * name, value & out) const {
        const node * n = this->find (name);
        if (!n) return false;
        out = n->_current;
        return true;
    }

    bool registry::set (const char * name, const value & nv) {
        value  old {};
        node * n = this->find (name);
        if (!n) return false;
        if ((n->_meta._flags & flags::F_WR) != flags::F_WR) return false;

        // 如果类型相同但数值不同, 或者新值类型未设置, 这时候设置新的数值
        if (((n->_current._type == nv._type) && (n->_current != nv)) || (nv._type == type::nil)) {
            old               = n->_current;
            n->_current       = nv;
            n->_current._type = old._type;
            this->notify (n->_meta, old, nv);
        }
        return true;
    }

    void registry::notify (const meta & m, const value & _old, const value & _new) {
        for (auto & cb : this->_cbs)
            if (cb) cb (m, _old, _new);
    }

}  // namespace paramfw

namespace paramfw {

    struct CRC32 {
        uint32_t v {0xFFFFFFFFu};

        void update (const void * data, uint32_t len) {
            const uint8_t * p = static_cast<const uint8_t *> (data);
            for (uint32_t i = 0; i < len; ++i) {
                v ^= p[i];
                for (int j = 0; j < 8; ++j) { v = (v & 1) ? (0xEDB88320u ^ (v >> 1)) : (v >> 1); }
            }
        }

        uint32_t current () const { return v ^ 0xFFFFFFFFu; }
    };

    struct __attribute__ ((packed, aligned (1))) Header {
        uint32_t magic;
        uint16_t ver;
        uint16_t cnt;
        uint32_t crc;
    };

    bool registry::save_impl () {

        // 计算总长度
        CRC32    crc;
        uint32_t off = 0;

        struct Header hdr {kMagic, kVersion, (uint16_t) this->_count, 0x00000000};

        // 预计算CRC
        for (uint32_t i = 0; i < this->_count; ++i) {
            const node & n = this->_nodes[i];
            if ((n._meta._flags & flags::F_PERSIST) != flags::F_PERSIST)
                continue;  // 跳过无"持久化"权限的参数

            crc.update (&n, sizeof (n));
        }

        hdr.crc = crc.current ();
        if (!this->_storage->erase ()) return false;
        if (!this->_storage->write (off, &hdr, sizeof (hdr))) return false;
        off += sizeof (hdr);

        for (uint32_t i = 0; i < this->_count; ++i) {
            const node & n = this->_nodes[i];
            if ((n._meta._flags & flags::F_PERSIST) != flags::F_PERSIST)
                continue;  // 跳过无"持久化"权限的参数

            if (!this->_storage->write (off, &n, sizeof (n)))
                return false;

            off += sizeof (n);
        }

        return true;
    }

    bool registry::load_impl () {
        struct Header hdr {};

        CRC32    crc;
        uint32_t off = 0;
        node     n;

        if (!this->_storage->read (off, &hdr, sizeof (hdr)))
            return false;

        off += sizeof (hdr);
        if (hdr.magic != kMagic) return false;

        for (auto i = 0; i < hdr.cnt; ++i) {
            if (!this->_storage->read (off, &n, sizeof (n)))
                return false;
            off += sizeof (n);
            crc.update (&n, sizeof (n));
            this->_nodes[i] = n;
        }
        this->_count = hdr.cnt;
        if (crc.current () != hdr.crc) return false;
        return true;
    }


}  // namespace paramfw

namespace paramfw {

    bool registry::get_as (const char * name, int8_t & out) const {
        value v {};
        if (!this->get (name, v) || v._type != type::s08) return false;
        out = v.s08;
        return true;
    }

    bool registry::get_as (const char * name, uint8_t & out) const {
        value v {};
        if (!this->get (name, v) || v._type != type::u08) return false;
        out = v.u08;
        return true;
    }

    bool registry::get_as (const char * name, int16_t & out) const {
        value v {};
        if (!this->get (name, v) || v._type != type::s16) return false;
        out = v.s16;
        return true;
    }

    bool registry::get_as (const char * name, uint16_t & out) const {
        value v {};
        if (!this->get (name, v) || v._type != type::u16) return false;
        out = v.u16;
        return true;
    }

    bool registry::get_as (const char * name, int32_t & out) const {
        value v {};
        if (!this->get (name, v) || v._type != type::s32) return false;
        out = v.s32;
        return true;
    }

    bool registry::get_as (const char * name, uint32_t & out) const {
        value v {};
        if (!this->get (name, v) || v._type != type::u32) return false;
        out = v.u32;
        return true;
    }

    bool registry::get_as (const char * name, int64_t & out) const {
        value v {};
        if (!this->get (name, v) || v._type != type::s64) return false;
        out = v.s64;
        return true;
    }

    bool registry::get_as (const char * name, uint64_t & out) const {
        value v {};
        if (!this->get (name, v) || v._type != type::u64) return false;
        out = v.u64;
        return true;
    }

    bool registry::get_as (const char * name, float & out) const {
        value v {};
        if (!this->get (name, v) || v._type != type::f32) return false;
        out = v.f32;
        return true;
    }

    inline bool registry::get_as (const char * name, double & out) const {
        value v {};
        if (!this->get (name, v) || v._type != type::f64) return false;
        out = v.f64;
        return true;
    }

    bool registry::get_as (const char * name, char (&out)[kMaxSize]) const {
        value v {};
        if (!this->get (name, v) || v._type != type::str) return false;
        std::strncpy (out, v.str, kMaxSize);
        return true;
    }

    bool registry::get_as (const char * name, uint8_t (&out)[kMaxSize]) const {
        value v {};
        if (!this->get (name, v)) return false;
        std::memcpy (out, v.buf, kMaxSize);
        return true;
    }

    bool registry::set_as (const char * name, const int8_t & nv) {
        return this->set (name, value {._type = type::s08, .s08 = nv});
    }

    bool registry::set_as (const char * name, const uint8_t & nv) {
        return this->set (name, value {._type = type::u08, .u08 = nv});
    }

    bool registry::set_as (const char * name, const int16_t & nv) {
        return this->set (name, value {._type = type::s16, .s16 = nv});
    }

    bool registry::set_as (const char * name, const uint16_t & nv) {
        return this->set (name, value {._type = type::u16, .u16 = nv});
    }

    bool registry::set_as (const char * name, const int32_t & nv) {
        return this->set (name, value {._type = type::s32, .s32 = nv});
    }

    bool registry::set_as (const char * name, const uint32_t & nv) {
        return this->set (name, value {._type = type::u32, .u32 = nv});
    }

    bool registry::set_as (const char * name, const int64_t & nv) {
        return this->set (name, value {._type = type::s64, .s64 = nv});
    }

    bool registry::set_as (const char * name, const uint64_t & nv) {
        return this->set (name, value {._type = type::u64, .u64 = nv});
    }

    bool registry::set_as (const char * name, const float & nv) {
        return this->set (name, value {._type = type::f32, .f32 = nv});
    }

    bool registry::set_as (const char * name, const double & nv) {
        return this->set (name, value {._type = type::f64, .f64 = nv});
    }

    bool registry::set_as (const char * name, char * const & nv) {
        if (!nv) return false;
        value v {._type = type::str};
        std::strncpy (v.str, nv, sizeof (v.str));
        return this->set (name, v);
    }

    bool registry::set_as (const char * name, const char * const & nv) {
        if (!nv) return false;
        value v {._type = type::str};
        std::strncpy (v.str, nv, sizeof (v.str));
        return this->set (name, v);
    }

    bool registry::set_as (const char * name, uint8_t * const & nv) {
        if (!nv) return false;
        value v {._type = type::str};
        std::strncpy (v.buf, reinterpret_cast<char *> (nv), sizeof (v.str));
        return this->set (name, v);
    }

}  // namespace paramfw

namespace paramfw {

    void print_value (const value & v) {
        switch (v._type) {
            case type::s08: return (void) std::printf ("[s08: %d]", v.s08);
            case type::u08: return (void) std::printf ("[u08: %u]", v.u08);
            case type::s16: return (void) std::printf ("[s16: %d]", v.s16);
            case type::u16: return (void) std::printf ("[u16: %u]", v.u16);
            case type::s32: return (void) std::printf ("[s32: %d]", v.s32);
            case type::u32: return (void) std::printf ("[u32: %u]", v.u32);
            case type::s64: return (void) std::printf ("[s64: %ld]", v.s64);
            case type::u64: return (void) std::printf ("[u64: %lu]", v.u64);
            case type::f32: return (void) std::printf ("[f32: %f]", v.f32);
            case type::f64: return (void) std::printf ("[f64: %f]", v.f64);
            default       : return (void) std::printf ("[nan: %s]", v.str);
        }
    }

    void print_meta (const meta & m) {
        (void) std::printf ("[");
        (void) std::printf ("F_NONE");

        if ((m._flags & flags::F_PERSIST) == flags::F_PERSIST) (void) std::printf ("|F_PERSIST");
        if ((m._flags & flags::F_RD) == flags::F_RD) (void) std::printf ("|F_RD");
        if ((m._flags & flags::F_WR) == flags::F_WR) (void) std::printf ("|F_WR");

        (void) std::printf ("] %s ", m._name.c_str ());
        // print_value (m._default);
    }

    void on_param_change (const meta & m, const value & oldv, const value & newv) {
        print_meta (m);
        print_value (oldv);
        (void) std::printf (" -> ");
        print_value (newv);
        (void) std::printf ("\n");
    }

}  // namespace paramfw
